//
// Copyright (c) 2009 Tridium, Inc.
// Licensed under the Academic Free License version 3.0
//
// History:
//   24 Feb 09  Dan Giorgis  Creation
//

#include "sedona.h"

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

#include <unistd.h>

#include "math.h"


// assumed that we have 12 digital output channel for use.

static float 	out_c=0;
static float 	out_hook_c=0;   // for timing computation

static int 		channel_c=0;
static bool 	reversePolarity_c=0;		// Voltage, 0 to 5v
static int 		minOnTime_c=0;
static int		minOffTime_c=0;
static int		interDelay_c=0;

// from the class of 'DigitalOutputStatus'

static int 		onCounter_c[12];
static int		offCounter_c[12];
static int		onTimer_c[12];
static int		offTimer_c[12];

// proririty index - 0: false, 1: true, 2: null (inactive)

static int	in1_c=2;
static int	in2_c=2;
static int	in3_c=2;
static int	in4_c=2;
static int	in5_c=2;
static int	in6_c=2;
static int	in7_c=2;
static int	in8_c=2;
static int	in9_c=2;
static int	in10_c=2;
static int	in11_c=2;
static int	in12_c=2;
static int	in13_c=2;
static int	in14_c=2;
static int	in15_c=2;
static int	in16_c=2;




static int 	relinquishDefault_c=0;


 struct timeval tv0_on[12], tv1_on[12],tv0_off[12], tv1_off[12];
 struct timezone tz0_on[12], tz1_on[12],tz0_off[12], tz1_off[12];

static  int kk0_on,kk1_on, kk0_off,kk1_off;
static  double timeDiff_on_c[12],timeDiff_off_c[12];

static int state_in_time[12];


static int channel_to_out[12];
static int channel_to_pri[12][16] = {2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2,
	2, 2, 2,2,2,2,2,2,2, 2, 2,2,2,2,2,2};





////////////////////////////////////////////////////////////////
// Native Methods
////////////////////////////////////////////////////////////////

//  Temparature (0 - 100 oC) <=> Voltage (0 - 3.3 v)
Cell vonplusBasic_dop_set(SedonaVM* vm, Cell* params)
{
	
       channel_c = params[0].ival;    

	 if (channel_c==0) 
	 	return zeroCell;
	 
	int pri_in_idx= params[1].ival;    
	int in= params[2].ival;  

	//printf(" dop set for channel %d, pri %d, in %d \n", channel_c, pri_in_idx, in);
	

	if (pri_in_idx==1) // only for the current and voltages (0, 1, 2, 3)
		in1_c= in;
	else if (pri_in_idx==2) 
		in2_c= in;
	else if (pri_in_idx==3) 
		in3_c= in;
	else if (pri_in_idx==4) 
		in4_c= in;
	else if (pri_in_idx==5) 
		in5_c= in;
	else if (pri_in_idx==6) 
		in6_c= in;
	else if (pri_in_idx==7) 
		in7_c= in;
	else if (pri_in_idx==8) 
		in8_c= in;
	else if (pri_in_idx==9) 
		in9_c= in;
	else if (pri_in_idx==10) 
		in10_c= in;
	else if (pri_in_idx==11) 
		in11_c= in;
	else if (pri_in_idx==12) 
		in12_c= in;
	else if (pri_in_idx==13) 
		in13_c= in;
	else if (pri_in_idx==14) 
		in14_c= in;
	else if (pri_in_idx==15) 
		in15_c= in;
	else if (pri_in_idx==16) 
		in16_c= in;


	//printf(" dop set for channel    ^^^^^^^^^^^^^^^   %d \n", channel_c);
	channel_to_pri[channel_c-1][pri_in_idx-1] = in;

	if (in1_c==2)
	{
	if (in2_c==2)
	{
	if (in3_c==2)
	{
	if (in4_c==2)
	{
	if (in5_c==2)
	{
	if (in6_c==2)
	{
	if (in7_c==2)
	{
	if (in8_c==2)
	{
	if (in9_c==2)
	{
	if (in10_c==2)
	{
	if (in11_c==2)
	{
	if (in12_c==2)
	{
	if (in13_c==2)
	{
	if (in14_c==2)
	{
	if (in15_c==2)
	{
	if (in16_c==2)
	{
			if ((channel_to_out[channel_c-1]==0)&&(relinquishDefault_c==1))
			{
				onCounter_c[channel_c-1]++;
			}
			else if ((channel_to_out[channel_c-1]==1)&&(relinquishDefault_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=relinquishDefault_c;

			//printf(" set for ===========  fallback \n");
	}
	else
	{
			if ((channel_to_out[channel_c-1]==0)&&(in16_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in16_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in16_c;

			//printf(" set for ===========  in16 \n");
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in15_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in15_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in15_c;

			//printf(" set for ===========  in15 \n");
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in14_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in14_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in14_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in13_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in13_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in13_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in12_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in12_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in12_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in11_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in11_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in11_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in10_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in10_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in10_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in9_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in9_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in9_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in8_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in8_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in8_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in7_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in7_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in7_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in6_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in6_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in6_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in5_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in5_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in5_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in4_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in4_c==0))
				offCounter_c[channel_c-1]++;

			out_c=in4_c;
		}
	
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in3_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in3_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in3_c;
		}
	}
	else
		{
			if ((channel_to_out[channel_c-1]==0)&&(in2_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in2_c==0))
				offCounter_c[channel_c-1]++;
			
			out_c=in2_c;
		}
	}
	else
	{
			if ((channel_to_out[channel_c-1]==0)&&(in1_c==1))
				onCounter_c[channel_c-1]++;
			else if ((channel_to_out[channel_c-1]==1)&&(in1_c==0))
				offCounter_c[channel_c-1]++;
				
			out_c=in1_c;
	}

	
	//printf(" dop set for channel    ############  %d \n", channel_c);

	// 3 state transfer diagram for on/off time

       // problems caused by changing the channel
       // how to solve the default value for prority ????
	
	if ((channel_to_out[channel_c-1]==0)&&(out_c==0)&&(state_in_time[channel_c-1] ==0))	
	{
		state_in_time[channel_c-1] = 1;
		kk0_off=gettimeofday(&tv0_off[channel_c-1], &tz0_off[channel_c-1]);

		//printf(" 1111111111 \n");
	}
	else if ((channel_to_out[channel_c-1]==0)&&(out_c==1)&&(state_in_time[channel_c-1] ==1))					
	{
		state_in_time[channel_c-1] = 2;
		kk0_on=gettimeofday(&tv0_on[channel_c-1], &tz0_on[channel_c-1]);

		//printf(" 22222222222 \n");
	}
	else if  ((channel_to_out[channel_c-1]==1)&&(out_c==0)&&(state_in_time[channel_c-1] ==2))	
        {
        	state_in_time[channel_c-1] = 1;
        	kk0_off=gettimeofday(&tv0_off[channel_c-1], &tz0_off[channel_c-1]);   	

			//printf(" 33333333333333 \n");
	}

//printf(" dop set for channel    ============= %d \n", channel_c);

	if ((channel_to_out[channel_c-1]==0)&&(out_c==0)&&(state_in_time[channel_c-1] ==1))	
	{
		kk1_off=gettimeofday(&tv1_off[channel_c-1], &tz1_off[channel_c-1]);	
		timeDiff_off_c[channel_c-1]= difftime(tv1_off[channel_c-1].tv_sec, tv0_off[channel_c-1].tv_sec);

		offTimer_c[channel_c-1]=timeDiff_off_c[channel_c-1];		

		//printf(" 444444444 =  %f\n", offTimer_c[channel_c-1]);
	}
	else if ((channel_to_out[channel_c-1]==1)&&(out_c==1)&&(state_in_time[channel_c-1] ==2))					
	{
		kk1_on=gettimeofday(&tv1_on[channel_c-1], &tz1_on[channel_c-1]);
		timeDiff_on_c[channel_c-1]= difftime(tv1_on[channel_c-1].tv_sec, tv0_on[channel_c-1].tv_sec);

		onTimer_c[channel_c-1]=timeDiff_on_c[channel_c-1];

		//printf(" 55555555555555 \n");
	}

	//printf("\n\n");
	//printf(" dop set for channel    @@@@@@@@  %d \n", channel_c);

	//printf("dop set out  1111 = %d, channel = %d \n", out_c, channel_c);

	out_hook_c = out_c;
	channel_to_out[channel_c-1]=out_c;

	//printf("dop set channel = %d \n", channel_c);
	

	int fd;
	fd = open("/dev/leds0", 0);
	//printf("fd1=  %d\n", fd);
	if (fd < 0) {
		fd = open("/dev/leds", 0);
		//printf("fd2=  %d\n", fd);
	}
	if (fd < 0) {
		perror("open device leds");
		exit(1);
	}

	//printf("dop set out 3333 = %d \n", out_c);

	//printf("\n\n");
	
	ioctl(fd, out_c, channel_c);
	close(fd);
	
	
  	 return zeroCell;

}

Cell vonplusBasic_dop_get(SedonaVM* vm, Cell* params)
{

	channel_c = params[0].ival;    

	//printf(" dop get channel %d\n", channel_c);
	
	  Cell result;

	  if (channel_c==0)
	  	result.ival = 0;
	  else
	 	result.ival = channel_to_out[channel_c-1]; 
	   
	  return result;

}



Cell vonplusBasic_dop_getStatus(SedonaVM* vm, Cell* params)
{
	printf("dop get status\n");
	channel_c = params[0].ival;    

	params[1].ival = 2; //onCounter_c[channel_c-1];
	params[2].ival = 2;//offCounter_c[channel_c-1];
	params[3].ival = onTimer_c[channel_c-1];
	params[4].ival = offTimer_c[channel_c-1];

	
	return zeroCell;
}

Cell vonplusBasic_dop_getStatusOnCounter(SedonaVM* vm, Cell* params)
{
	
	channel_c = params[0].ival;    

	printf("dop get status on counter %d, channel = %d\n", onCounter_c[channel_c-1], channel_c);

	Cell result;

	  if (channel_c==0)
	  	result.ival = 0;
	  else
	 	result.ival = onCounter_c[channel_c-1]; 	
	
	return result;
}

Cell vonplusBasic_dop_getStatusOffCounter(SedonaVM* vm, Cell* params)
{
	
	channel_c = params[0].ival;    

	printf("dop get status off counter %d, channel = %d\n", offCounter_c[channel_c-1], channel_c);

	Cell result;

	  if (channel_c==0)
	  	result.ival = 0;
	  else
	 	result.ival = offCounter_c[channel_c-1]; 	

	  
	
	return result;
}

Cell vonplusBasic_dop_getStatusOnTimer(SedonaVM* vm, Cell* params)
{
	
	channel_c = params[0].ival;    

	// off timer updating ...
	
	if ((channel_to_out[channel_c-1]==0)&&(out_hook_c==0)&&(state_in_time[channel_c-1] ==1))	
	{
		kk1_off=gettimeofday(&tv1_off[channel_c-1], &tz1_off[channel_c-1]);	
		timeDiff_off_c[channel_c-1]= difftime(tv1_off[channel_c-1].tv_sec, tv0_off[channel_c-1].tv_sec);

		offTimer_c[channel_c-1]=timeDiff_off_c[channel_c-1];		

		//printf(" 6666666666 =  %f\n", offTimer_c[channel_c-1]);
	}

	printf("dop get status on timer %d, channel = %d\n", onTimer_c[channel_c-1], channel_c);

	Cell result;

	  if (channel_c==0)
	  	result.ival = 0;
	  else
	 	result.ival = onTimer_c[channel_c-1]; 	


	return result;
}

Cell vonplusBasic_dop_getStatusOffTimer(SedonaVM* vm, Cell* params)
{
	
	channel_c = params[0].ival;    


	if ((channel_to_out[channel_c-1]==1)&&(out_hook_c==1)&&(state_in_time[channel_c-1] ==2))					
	{
		kk1_on=gettimeofday(&tv1_on[channel_c-1], &tz1_on[channel_c-1]);
		timeDiff_on_c[channel_c-1]= difftime(tv1_on[channel_c-1].tv_sec, tv0_on[channel_c-1].tv_sec);

		onTimer_c[channel_c-1]=timeDiff_on_c[channel_c-1];

		//printf(" 7777777777777 =  %f \n",onTimer_c[channel_c-1]);
	}


	

	printf("dop get status off timer %d, channel = %d\n", offTimer_c[channel_c-1], channel_c);

	Cell result;

	  if (channel_c==0)
	  	result.ival = 0;
	  else
	 	result.ival = offTimer_c[channel_c-1]; 	

	
	return result;
}


Cell vonplusBasic_dop_setRelinquishDefault(SedonaVM* vm, Cell* params)
{
       channel_c = params[0].ival;    
	
	int in= params[1].ival;  
	
	relinquishDefault_c= in;
	
	printf(" dop set fallback\n");


	if ((channel_to_pri[channel_c-1][0]==2)&&(channel_to_pri[channel_c-1][1]==2)&&
		(channel_to_pri[channel_c-1][2]==2)&&(channel_to_pri[channel_c-1][3]==2)&&
		(channel_to_pri[channel_c-1][4]==2)&&(channel_to_pri[channel_c-1][5]==2)&&
		(channel_to_pri[channel_c-1][6]==2)&&(channel_to_pri[channel_c-1][7]==2)&&
		(channel_to_pri[channel_c-1][8]==2)&&(channel_to_pri[channel_c-1][9]==2)&&
		(channel_to_pri[channel_c-1][10]==2)&&(channel_to_pri[channel_c-1][11]==2)&&
		(channel_to_pri[channel_c-1][12]==2)&&(channel_to_pri[channel_c-1][13]==2)&&
		(channel_to_pri[channel_c-1][14]==2)&&(channel_to_pri[channel_c-1][15]==2))		
		{ 

			channel_to_out[channel_c-1] = relinquishDefault_c; 

			int fd;
			fd = open("/dev/leds0", 0);
			if (fd < 0) {
				fd = open("/dev/leds", 0);
			}
			if (fd < 0) {
				perror("open device leds");
				exit(1);
			}

			ioctl(fd, relinquishDefault_c, channel_c);
			close(fd);

		}


	
	return zeroCell;
}
	

Cell vonplusBasic_dop_setPolarity(SedonaVM* vm, Cell* params)
{
        channel_c = params[0].ival;    
	
	int in= params[1].ival;  
	
	reversePolarity_c= in;

	printf(" dop set polarity\n");
  
  	 return zeroCell;

}

Cell vonplusBasic_dop_getPriorityStatus(SedonaVM* vm, Cell* params)
{

        channel_c= params[0].ival;    
	int	pri_idx= params[1].ival; 

  	Cell result;

	  if (channel_c==0)
	  	result.ival = 2;
	  else
	  	result.ival = channel_to_pri[channel_c-1][pri_idx-1]; 

	  //printf(" get priority status channel %d, pri_idx %d, channel pri %d\n", channel_c, pri_idx, channel_to_pri[channel_c-1][pri_idx-1]);

  
  	 return result;
}

Cell vonplusBasic_dop_setMinimumOnTime(SedonaVM* vm, Cell* params)
{
	 channel_c = params[0].ival;    
	
	int in= params[1].ival;  
	
	minOnTime_c= in;

	printf(" dop set minimum\n");
  
  	 return zeroCell;
}

Cell vonplusBasic_dop_setMinimumOffTime(SedonaVM* vm, Cell* params)
{
	 channel_c = params[0].ival;    
	
	int in= params[1].ival;  
	
	minOffTime_c= in;
  
  	 return zeroCell;
}

Cell vonplusBasic_dop_resetOnCounter(SedonaVM* vm, Cell* params)
{
	channel_c= params[0].ival;    

	onCounter_c[channel_c-1]= 0;

	
  	 return zeroCell;
}

Cell vonplusBasic_dop_resetOffCounter(SedonaVM* vm, Cell* params)
{

	channel_c= params[0].ival;    

	offCounter_c[channel_c-1]= 0;

  	 return zeroCell;
}

Cell vonplusBasic_dop_resetOnTimer(SedonaVM* vm, Cell* params)
{

	channel_c= params[0].ival;    

	onTimer_c[channel_c-1]= 0;

  	 return zeroCell;
}

Cell vonplusBasic_dop_resetOffTimer(SedonaVM* vm, Cell* params)
{
	channel_c= params[0].ival;    

	onTimer_c[channel_c-1]= 0;

  	 return zeroCell;
}

Cell vonplusBasic_dop_setInterDelayTime(SedonaVM* vm, Cell* params)
{

	 channel_c = params[0].ival;    
	
	int in= params[1].ival;  
	
	interDelay_c= in;

  	 return zeroCell;
}
